package io.xxx.omni.sync.impl

import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONObject
import io.xxx.omni.sync.Document
import io.xxx.omni.sync.PagePorter
import io.xxx.omni.sync.Store
import io.xxx.omni.util.dateTimeFormatter
import io.xxx.omni.util.toLocalDateTime
import org.apache.commons.codec.digest.DigestUtils
import org.slf4j.LoggerFactory
import org.springframework.http.HttpEntity
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpMethod
import org.springframework.http.MediaType
import org.springframework.stereotype.Component
import org.springframework.web.util.UriComponentsBuilder
import java.time.Duration
import java.time.LocalDateTime
import java.util.*

/**
 * 云集
 */
abstract class YjPorter : PagePorter() {

    private val httpUrl = "https://op.yunjiglobal.com/opgateway/api/openapi"

    override val platformCode = "yunji"

    protected abstract val method: String

    override val duration: Duration? = Duration.ofDays(1)

    protected fun getResponse(
        store: Store,
        method: String,
        params: Map<String, String> = emptyMap()
    ): JSONObject {
        val signMap = TreeMap<String, String>()
        signMap["method"] = method
        signMap["timestamp"] = LocalDateTime.now().format(dateTimeFormatter)
        signMap["format"] = "json"
        signMap["app_key"] = store.appKey!!
        signMap["v"] = "3.0"
        signMap["sign_method"] = "md5"
        signMap["session"] = store.accessToken!!
        val builder = StringBuilder().append(store.appSecret)
        signMap.forEach { (k, v) -> builder.append(k).append(v) }
        builder.append(JSON.toJSONString(params)).append(store.appSecret)
        val sign = DigestUtils.md5Hex(builder.toString()).toUpperCase()
        val urlBuilder = UriComponentsBuilder.fromHttpUrl(httpUrl)
        signMap.forEach { urlBuilder.queryParam(it.key, it.value) }
        val url = urlBuilder.queryParam("sign", sign).build(false).toUri()
        val headers = HttpHeaders()
        headers.contentType = MediaType.APPLICATION_JSON
        val requestEntity = HttpEntity<Any>(params, headers)
        val response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, JSONObject::class.java)
        if (response.statusCode.isError || response.body == null) {
            val msg = "接口[method: $method, queryParams: $signMap]异常"
            throw RuntimeException(msg)
        }
        val body = response.body!!
        if (body.getString("flag") == "failure") {
            val msg = "接口[method: $method, queryParams: $signMap]异常: $body"
            log.error(msg)
            throwException(
                method,
                body.getString("sub_code") ?: body.getString("code"),
                body.getString("sub_message") ?: body.getString("message")
            )
        }
        return response.body!!.getJSONObject("data")
    }

    protected fun getParams(
        startTime: LocalDateTime,
        endTime: LocalDateTime,
        queryType: Int,
        pageNo: Long = 1
    ): Map<String, String> {
        return mapOf(
            "query_type" to queryType.toString(),
            "start_modified" to startTime.format(dateTimeFormatter),
            "end_modified" to endTime.format(dateTimeFormatter),
            "page_no" to pageNo.toString(),
            "page_size" to pageSize.toString(),
        )
    }

    companion object {
        private val log = LoggerFactory.getLogger(XhsPagePorter::class.java)
    }
}

@Component
class YjTradePorter : YjPorter() {

    override val method = "pop.order.list.get"

    override val jobId = 1

    override fun getParameters(store: Store): List<Any> {
        return listOf(1)
    }

    override fun getCount(store: Store, startTime: LocalDateTime, endTime: LocalDateTime, parameter: Any?): Long? {
        val params = getParams(startTime, endTime, parameter as Int)
        val response = getResponse(store, method, params)
        return response.getLong("total")
    }

    override fun getData(
        store: Store,
        startTime: LocalDateTime,
        endTime: LocalDateTime,
        parameter: Any?,
        pageNo: Long,
    ): List<Document>? {
        val params = getParams(startTime, endTime, parameter as Int, pageNo)
        val response = getResponse(store, method, params)
        return response.getJSONArray("lists")?.map {
            @Suppress("unchecked_cast")
            val value = JSONObject(it as Map<String, Any>)
            Document(
                value.getString("order_id"),
                value,
                value.getLong("modify_time").toLocalDateTime()
            )
        }
    }
}

@Component
class YjTradeProcurator : YjTradePorter() {

    override val jobId = 3

    override val cron = "0/10 * * * * ?"

    override fun getParameters(store: Store): List<Any> {
        return listOf(0)
    }
}


@Component
class YjRefundPorter : YjPorter() {

    override val method = "pop.refund.list.get"

    override val jobId = 2

    override val duration: Duration? = Duration.ofHours(12)

    override fun getParameters(store: Store): List<Any> {
        return listOf(1)
    }

    override fun getCount(store: Store, startTime: LocalDateTime, endTime: LocalDateTime, parameter: Any?): Long? {
        val params = getParams(startTime, endTime, parameter as Int)
        val response = getResponse(store, method, params)
        return response.getLong("total_results")
    }

    override fun getData(
        store: Store,
        startTime: LocalDateTime,
        endTime: LocalDateTime,
        parameter: Any?,
        pageNo: Long,
    ): List<Document>? {
        val params = getParams(startTime, endTime, parameter as Int, pageNo)
        val response = getResponse(store, method, params)
        return response.getJSONArray("refunds")?.map {
            @Suppress("unchecked_cast")
            val value = JSONObject(it as Map<String, Any>)
            Document(
                value.getString("refund_id"),
                value.getString("order_id"),
                value,
                value.getLong("modify_time").toLocalDateTime()
            )
        }
    }
}

@Component
class YjRefundProcurator : YjRefundPorter() {

    override val jobId = 4

    override val cron = "0/10 * * * * ?"

    override fun getParameters(store: Store): List<Any> {
        return listOf(0)
    }
}